package com.dc.commons.jpa.repository.support;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import com.dc.commons.enumer.MoveType;
import com.dc.commons.exception.DcBusinessException;
import com.dc.commons.jpa.TicketWapper;
import com.dc.commons.jpa.repository.AuditAbleRepository;
import com.dc.commons.jpa.repository.DataStatusAbleRepository;
import com.dc.commons.jpa.repository.TreeAbleRepository;
import com.dc.commons.jpa.repository.UseAbleRepository;
import com.dc.commons.jpa.specification.SpecificationHelper;
import com.dc.commons.persistence.able.AuditAble;
import com.dc.commons.persistence.able.DataStatusAble;
import com.dc.commons.persistence.able.IDAble;
import com.dc.commons.persistence.able.TreeAble;
import com.dc.commons.persistence.able.UseAble;
import com.dc.commons.spring.SpringBeanFactory;
import com.dc.commons.status.AuditStatus;
import com.dc.commons.status.DataStatus;
import com.dc.commons.status.UseStatus;
import com.google.common.collect.Maps;

/*******************************************************************************<P>
 * 文件名: DcJpaRepository.java <br>
 * 版本: 1.0<br>
 * 描述: JpaRepository 具体实现类<br>
 * 版权所有: <br>
 * <P>
 * 创建者: 王建栋 <br>
 * 创建日期: 2015年12月22日 下午11:13:17<br>
 * 修改者: <br>
 * 修改日期: <br>
 * 修改说明: <br>
 ******************************************************************************/
public class DcJpaRepository<T extends IDAble & UseAble & AuditAble & DataStatusAble & TreeAble ,ID extends Serializable> 
								extends SimpleJpaRepository<T, ID> 
								implements	UseAbleRepository<UseAble,ID>, 
											AuditAbleRepository<AuditAble,ID>,
											DataStatusAbleRepository<DataStatusAble,ID>,
											TreeAbleRepository<TreeAble, ID>{
	
	
	private static Map<String,Class<?>> classMap=Maps.newConcurrentMap();
	private final EntityManager entityManager;
	protected final Class<? extends IDAble> clazz;
	
	@SuppressWarnings("unchecked")
	public DcJpaRepository(JpaEntityInformation<? extends IDAble, ?> entityMetadata,
			EntityManager entityManager) {
		super((JpaEntityInformation<T, ?>) entityMetadata, entityManager);
		this.entityManager=entityManager;
		this.clazz=entityMetadata.getJavaType();
		classMap.put(clazz.getSimpleName(), clazz);
	}

	public DcJpaRepository(Class<T> domainClass, EntityManager entityManager) {
		super(domainClass, entityManager);
		this.entityManager=entityManager;
		this.clazz=domainClass;
	}
	
	
	//******************************* AUDITABLE START***************************************************/
	
	
	/**
	 * useAble的预处理
	 * @param entity
	 * @param useStatus
	 * @return <S extends UseAble>S
	 */
	@Transactional
	private <S extends AuditAble>S preAuditAble(S entity,AuditStatus auditStatus){
		
		@SuppressWarnings("unchecked")
		S auditAble=(S) this.entityManager.find(clazz, entity.getId());
		
		TicketWapper ticketWapper=SpringBeanFactory.getBean(TicketWapper.class);
		
		auditAble.setAuditKey(ticketWapper.getMember());
		auditAble.setAuditDate(ticketWapper.getNow());
		auditAble.setAuditNote(entity.getAuditNote());
		auditAble.setAuditStatus(auditStatus);
		
		//TODO  这里可以记录日志
		
		return auditAble;
	}
	
	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.AuditAbleDao#submit(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends AuditAble>S submit(S entity) {
		AuditStatus audit=AuditStatus.WAIT_AUDIT;
		//TODO 这里可以在实体上设置初始状态或者 根据实体的真实状态进行设置
		entity=preAuditAble(entity, audit);
		S resut=this.entityManager.merge(entity);
		return resut;
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.AuditAbleDao#pass(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends AuditAble> S pass(S entity) {
		entity=preAuditAble(entity, AuditStatus.AUDIT_PASS);
		S resut=this.entityManager.merge(entity);
		return resut;
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.AuditAbleDao#noPass(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends AuditAble> S noPass(S entity) {
		entity=preAuditAble(entity, AuditStatus.AUDIT_NO_PASS);
		S resut=this.entityManager.merge(entity);
		return resut;
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.AuditAbleDao#callBack(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends AuditAble> S callBack(S entity) {
		entity=preAuditAble(entity, AuditStatus.AUDIT_BACK);
		S resut=this.entityManager.merge(entity);
		return resut;
	}

	//********************UseAble**********************************************/
	
	
	/**
	 * useAble的预处理
	 * @param entity
	 * @param useStatus
	 * @return <S extends UseAble>S
	 */
	@Transactional
	private <S extends UseAble>S preUseAble(S entity,UseStatus useStatus){
		
		@SuppressWarnings("unchecked")
		S useAble=(S) this.entityManager.find(clazz, entity.getId());
		
		TicketWapper ticketWapper=SpringBeanFactory.getBean(TicketWapper.class);
		
		useAble.setUseKey(ticketWapper.getMember());
		useAble.setUseDate(ticketWapper.getNow());
		if(useAble.getUseStatus()==UseStatus.DE_ACTIVATE){
			throw new DcBusinessException("实体已经停用，不可以再进行操作!");
		}
		
		useAble.setUseStatus(useStatus);
		
		//TODO  这里可以记录日志
		
		return useAble;
	}
	
	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.UseAbleDao#deActivate(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends UseAble> S deActivate(S entity) {
		
		entity=preUseAble(entity,UseStatus.DE_ACTIVATE);
		S resut=this.entityManager.merge(entity);
		return resut;
	}
	
	

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.UseAbleDao#enabled(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends UseAble> S enabled(S entity) {
		entity=preUseAble(entity,UseStatus.ENABLED);
		S resut=this.entityManager.merge(entity);
		return resut;
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.UseAbleDao#suspend(com.dc.commons.persistence.able.AuditAble)
	 */
	@Override
	@Transactional
	public <S extends UseAble> S suspend(S entity) {
		entity=preUseAble(entity,UseStatus.SUSPEND);
		S resut=this.entityManager.merge(entity);
		return resut;
	}
	
	//*************************DataStatus*********************************/

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.DataStatusAbleDao#remove(com.dc.commons.persistence.able.DataStatusAble)
	 */
	@SuppressWarnings("unchecked")
	@Override
	@Transactional
	public void remove(DataStatusAble t) {
		removeById((ID) t.getId());
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.DataStatusAbleDao#removeAll(java.util.List)
	 */
	@Override
	@Transactional
	public void removeAll(List<DataStatusAble> list) {
		for(DataStatusAble t:list){
			remove(t);
		}
		
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.DataStatusAbleDao#removeById(java.io.Serializable)
	 */
	@Override
	@Transactional
	public void removeById(ID id) {
		DataStatusAble dataAble=(DataStatusAble) this.findOne(id);
		dataAble.setDataStatus(DataStatus.INVALID);
		this.entityManager.merge(dataAble);
		
	}

	/* (non-Javadoc)
	 * @see com.dc.commons.jpa.repository.DataStatusAbleDao#removeByIds(java.util.List)
	 */
	@Override
	@Transactional
	public void removeByIds(List<ID> idList) {
		for(ID id:idList){
			removeById(id);
		}
	}

	//*****************************TreeAble start********************************/
	
	@Override
	@Transactional
	public <S extends TreeAble> S saveTree(S entity) {
		Specification<T> spc=SpecificationHelper.treeSpecification(entity.getParentKey(), entity.getDisplayNo());
		if(entity.getDisplayNo()==0){
			Long count=this.count(spc);
			entity.setDisplayNo(count.intValue()+1);
		}
		this.entityManager.persist(entity);
		return entity;
	}

	@Override
	@Transactional
	public <S extends TreeAble> S updateTree(S entity) {
		
		this.entityManager.merge(entity);
		
		return entity;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional
	public <S extends TreeAble> S deleteTree(S entity){
		this.delete((ID) entity.getId());
		return entity;
	}
	
	
	@Override
	@Transactional
	public void reduceSibling(String parentKey, Integer displayNo) {
		Specification<T> treeSpe=SpecificationHelper.treeSpecification(parentKey, displayNo);
		List<T> list=this.findAll(treeSpe);
		for(TreeAble treeAble:list){
			treeAble.setDisplayNo(treeAble.getDisplayNo()-1);
		}
	}

	@Override
	@Transactional
	public void affixationSibling(String parentKey, Integer displayNo) {
		Specification<T> treeSpe=SpecificationHelper.treeSpecification(parentKey, displayNo);
		List<T> list=this.findAll(treeSpe);
		for(TreeAble treeAble:list){
			treeAble.setDisplayNo(treeAble.getDisplayNo()+1);
		}
		
	}

	@Override
	@Transactional
	public void drag(ID from, ID to, MoveType moveType) {
		TreeAble fromNode=this.findOne(from);
		TreeAble toNode=this.findOne(to);
		
		reduceSibling(fromNode.getParentKey(), fromNode.getDisplayNo()+1);
		switch (moveType) {
		case prev:
				fromNode.setDisplayNo(toNode.getDisplayNo());
				fromNode.setLevelNo(toNode.getLevelNo());
				fromNode.setParentKey(toNode.getParentKey());
				affixationSibling(toNode.getParentKey(),toNode.getDisplayNo());
			break;
		case next:
			affixationSibling(toNode.getParentKey(),toNode.getDisplayNo()+1);
			fromNode.setDisplayNo(toNode.getDisplayNo()+1);
			fromNode.setLevelNo(toNode.getLevelNo());
			fromNode.setParentKey(toNode.getParentKey());
			break;
		case inner:
			fromNode.setLevelNo(toNode.getLevelNo()+1);
			fromNode.setParentKey(toNode.getId());
			Specification<T> treeSpe=SpecificationHelper.treeSpecification(toNode.getId(),0);
			Long max=this.count(treeSpe);
			fromNode.setDisplayNo(max.intValue()+1);
			break;
		default:
			break;
		} 
		
	}
	
}
